package com.uinnova.product.eam.service.es;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.util.BinaryUtils;
import com.uino.bean.cmdb.base.ESCIHistoryInfo;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.dao.AbstractESBaseDao;
import com.uino.dao.ESConst;
import com.uino.util.sys.BeanUtil;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

/**
 * ES-CI历史
 * 
 * @author zoumengjie
 */
@Service
public class IamsESCIHistoryDesignSvc extends AbstractESBaseDao<ESCIHistoryInfo, JSONObject> {

    private static final Logger log = LoggerFactory.getLogger(IamsESCIHistoryDesignSvc.class);

    @Override
    public String getIndex() {
        return ESConst.INDEX_CMDB_CI_HISTORY + "_design";
    }

    @Override
    public String getType() {
        return ESConst.INDEX_CMDB_CI_HISTORY + "_design";
    }

    @PostConstruct
    public void init() {
        super.initIndex();
    }

    @Override
    public Long saveOrUpdate(JSONObject obj, boolean isRefresh) {
        return this.saveOrUpdateWithPrimaryKey(obj, isRefresh, "uniqueCode");
    }

    @Override
    public Map<String, Object> saveOrUpdateBatchMessage(JSONArray list, Boolean isAsync) {
        return this.saveOrUpdateBatchMessageWithPrimaryKey(list, isAsync, "uniqueCode");
    }

    public Long saveOrUpdateHistoryInfo(ESCIInfo ciInfo, ESCIHistoryInfo.ActionType action) {
        if (action == ESCIHistoryInfo.ActionType.SAVE_OR_UPDATE) {
            Map<String, Long> ciCodeMaxVersion = this.getCICodeMaxVersion(Collections.singletonList(ciInfo.getCiCode()));
            // todo 新增CI与历史库ciCode相同时版本号递增？当ciCode相同，所属分类不同时会有问题，还没想好如何避免
            // 此处修改ciInfo对象，CI保存时版本号已更新，无需维护

            /*
            *   设计库历史表中保存的version字段如果根据设计库的version来 在数据导入导出删除再导入时会混乱
            *   这里还是单独维护一下吧 保证生成的历史数据列表顺序唯一且递增
            *
            * */
            Long version = ciCodeMaxVersion.get(ciInfo.getCiCode());
            if (version != null) {
                log.info("########## 历史库ciCode：【{}】，对应历史库最大版本号：【{}】", ciInfo.getCiCode(), version);
                ciInfo.setVersion(version + 1);
            }

        }
        ESCIHistoryInfo historyInfo = this.buildCIHistoryInfo(ciInfo, action);
        return this.saveOrUpdate(historyInfo);
    }

    public Integer saveOrUpdateHistoryInfosBatch(List<ESCIInfo> ciInfos, ESCIHistoryInfo.ActionType action) {
        if (!BinaryUtils.isEmpty(ciInfos)) {
            JSONArray historyInfos = new JSONArray();

            // 这里的批量保存和单个保存的version逻辑相同 历史表的version数据自己维护
            List<String> ciCodes = ciInfos.stream().map(ESCIInfo::getCiCode).collect(Collectors.toList());
            Map<String, Long> ciCodeMaxVersion = this.getCICodeMaxVersion(ciCodes);
            for (ESCIInfo ciInfo : ciInfos) {
                if (action == ESCIHistoryInfo.ActionType.SAVE_OR_UPDATE) {
                    // 新增CI与历史库ciCode相同时版本号递增
                    Long version = ciCodeMaxVersion.get(ciInfo.getCiCode());
                    if (version != null) {
                        log.info("########## 历史库ciCode：【{}】，对应历史库最大版本号：【{}】", ciInfo.getCiCode(), version);
                        ciInfo.setVersion(version + 1);
                    }
                }
                ESCIHistoryInfo historyInfo = this.buildCIHistoryInfo(ciInfo, action);
                String jsonStr = JSON.toJSONString(historyInfo);
                JSONObject json = JSON.parseObject(jsonStr);
                historyInfos.add(json);
            }
            // 这里在发布数据的时候 需要及时获取到最新版本的历史数据 需要做成实时处理
            this.saveOrUpdateBatchMessage(historyInfos, false);
        }
        return 1;
    }

    private ESCIHistoryInfo buildCIHistoryInfo(ESCIInfo esciInfo, ESCIHistoryInfo.ActionType action) {
        action = action == null ? ESCIHistoryInfo.ActionType.SAVE_OR_UPDATE : action;
        ESCIHistoryInfo historyInfo = ESCIHistoryInfo.builder().uniqueCode(esciInfo.getId() + "_" + esciInfo.getVersion()).version(esciInfo.getVersion()).action(action.getValue()).build();
        BeanUtil.copyProperties(esciInfo, historyInfo, "createTime", "modifyTime");
        Map<String, Object> attrs = new HashMap<String, Object>();
        Iterator<Entry<String, Object>> it = historyInfo.getAttrs().entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, Object> entry = it.next();
            String key = entry.getKey();
            Object val = entry.getValue() == null ? "" : entry.getValue();
            if (val instanceof Number) {
                attrs.put(key, new BigDecimal(val.toString()).toString());
            } else if (val instanceof String) {
                attrs.put(key, val);
            } else {
                attrs.put(key, JSON.toJSONString(val));
            }
        }
        historyInfo.setAttrs(attrs);
        return historyInfo;
    }

    public Map<String, Long> getCICodeMaxVersion(List<String> ciCodes) {
        Map<String, Long> res = new HashMap<>();
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        if(!BinaryUtils.isEmpty(ciCodes)){
            query.must(QueryBuilders.termsQuery("ciCode.keyword", ciCodes));
        }
        Map<String, BigDecimal> rltIdMaxVersionMap = super.groupByFieldMaxVal("ciCode.keyword", "version", query);
        if (rltIdMaxVersionMap != null && rltIdMaxVersionMap.size() > 0) {
            rltIdMaxVersionMap.forEach((key, val) -> res.put(key, val.longValue()));
        }
        return res;
    }

    public Map<String, Long> getMaxVersionByPrimaryKey(List<String> primaryKeys) {
        Map<String, Long> res = new HashMap<>();
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        if(!BinaryUtils.isEmpty(primaryKeys)){
            query.must(QueryBuilders.termsQuery("ciPrimaryKey.keyword", primaryKeys));
        }
        Map<String, BigDecimal> rltIdMaxVersionMap = super.groupByFieldMaxVal("ciPrimaryKey.keyword", "version", query);
        if (rltIdMaxVersionMap != null && rltIdMaxVersionMap.size() > 0) {
            rltIdMaxVersionMap.forEach((key, val) -> res.put(key, val.longValue()));
        }
        return res;
    }
}
